package com.example.demo.util;

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

enum ChatMessageRole {
    SYSTEM, USER, ASSISTANT;

    public String value() {
        return this.name().toLowerCase();
    }
}

class ChatCompletionMessage {
    public String role;
    public String name;
    public String content;
    public Boolean partial;

    public ChatCompletionMessage(String role, String content) {
        this.role = role;
        this.content = content;
    }

    public ChatCompletionMessage(String role, String name, String content, Boolean partial) {
        this.role = role;
        this.name = name;
        this.content = content;
        this.partial = partial;
    }

    public String getName() {
        return name;
    }

    public String getContent() {
        return content;
    }

    public Boolean getPartial() {
        return partial;
    }
}

class ChatCompletionStreamChoiceDelta {
    private String content;
    private String role;

    public String getContent() {
        return content;
    }

    public String getRole() {
        return role;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public ChatCompletionStreamChoiceDelta(String content, String role) {
        this.content = content;
        this.role = role;
    }
}

class Usage {
    private int promptTokens;
    private int completionTokens;
    private int totalTokens;

    public int getPromptTokens() {
        return promptTokens;
    }

    public int getCompletionTokens() {
        return completionTokens;
    }

    public int getTotalTokens() {
        return totalTokens;
    }
}

class ChatCompletionStreamChoice {
    private int index;
    private ChatCompletionStreamChoiceDelta delta;

    @SerializedName("finish_reason")
    private String finishReason;
    private Usage usage;

    public int getIndex() {
        return index;
    }

    public ChatCompletionStreamChoiceDelta getDelta() {
        return delta;
    }

    public String getFinishReason() {
        return finishReason;
    }

    public Usage getUsage() {
        return usage;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void setDelta(ChatCompletionStreamChoiceDelta delta) {
        this.delta = delta;
    }

    public void setFinishReason(String finishReason) {
        this.finishReason = finishReason;
    }

    public void setUsage(Usage usage) {
        this.usage = usage;
    }

    public ChatCompletionStreamChoice(int index, ChatCompletionStreamChoiceDelta delta, String finishReason, Usage usage) {
        this.index = index;
        this.delta = delta;
        this.finishReason = finishReason;
        this.usage = usage;
    }
}

class ChatCompletionStreamResponse {
    private String id;
    private String object;
    private long created;
    private String model;
    private List<ChatCompletionStreamChoice> choices;

    public String getId() {
        return id;
    }

    public String getObject() {
        return object;
    }

    public long getCreated() {
        return created;
    }

    public String getModel() {
        return model;
    }

    public List<ChatCompletionStreamChoice> getChoices() {
        return choices;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setObject(String object) {
        this.object = object;
    }

    public void setCreated(long created) {
        this.created = created;
    }
}

class ChatCompletionChoice {
    private int index;
    private ChatCompletionMessage message;

    @SerializedName("finish_reason")
    private String finishReason;

    public int getIndex() {
        return index;
    }

    public ChatCompletionMessage getMessage() {
        return message;
    }

    public String getFinishReason() {
        return finishReason;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void setMessage(ChatCompletionMessage message) {
        this.message = message;
    }

    public void setFinishReason(String finishReason) {
        this.finishReason = finishReason;
    }

}

class ChatCompletionResponse {
    private String id;
    private String object;
    private long created;
    private String model;
    private List<ChatCompletionChoice> choices;
    private Usage usage;

    public String getId() {
        return id;
    }

    public String getObject() {
        return object;
    }

    public long getCreated() {
        return created;
    }

    public String getModel() {
        return model;
    }

    public List<ChatCompletionChoice> getChoices() {
        if (choices == null) {
            return List.of();
        }
        return choices;
    }
}


class ChatCompletionRequest {
    public String model;
    public List<ChatCompletionMessage> messages;

    @SerializedName("max_tokens")
    public int maxTokens;

    @SerializedName("temperature")
    public float temperature;
    public float topP;

    public Integer n;
    public boolean stream;
    public List<String> stop;

    @SerializedName("presence_penalty")
    public float presencePenalty;

    @SerializedName("frequency_penalty")
    public float frequencyPenalty;

    public String user;

    public List<ChatCompletionMessage> getMessages() {
        return messages;
    }

    public ChatCompletionRequest(String model, List<ChatCompletionMessage> messages, int maxTokens, float temperature, int n) {
        this.model = model;
        this.messages = messages;
        this.maxTokens = maxTokens;
        this.temperature = temperature;
        this.n = n;
    }

}

class Model {
    private String id;
    private String object;

    @SerializedName("owner_by")
    private String ownedBy;
    private String root;
    private String parent;

    public String getId() {
        return id;
    }

    public String getObject() {
        return object;
    }

    public String getOwnedBy() {
        return ownedBy;
    }

    public String getRoot() {
        return root;
    }

    public String getParent() {
        return parent;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setObject(String object) {
        this.object = object;
    }

    public void setOwnedBy(String ownedBy) {
        this.ownedBy = ownedBy;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    public void setParent(String parent) {
        this.parent = parent;
    }

    public Model(String id, String object, String ownedBy, String root, String parent) {
        this.id = id;
        this.object = object;
        this.ownedBy = ownedBy;
        this.root = root;
        this.parent = parent;
    }
}

class ModelsList {
    private List<Model> data;

    public List<Model> getData() {
        return data;
    }

    public void setData(List<Model> data) {
        this.data = data;
    }

    public ModelsList(List<Model> data) {
        this.data = data;
    }
}

class Client {
    private static final String DEFAULT_BASE_URL = "https://api.moonshot.cn/v1";

    private static final String CHAT_COMPLETION_SUFFIX = "/chat/completions";
    private static final String MODELS_SUFFIX = "/models";
    private static final String FILES_SUFFIX = "/files";

    private String baseUrl;
    private String apiKey;

    public Client(String apiKey) {
        this(apiKey, DEFAULT_BASE_URL);
    }

    public Client(String apiKey, String baseUrl) {
        this.apiKey = apiKey;
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        this.baseUrl = baseUrl;
    }

    public String getChatCompletionUrl() {
        return baseUrl + CHAT_COMPLETION_SUFFIX;
    }

    public String getModelsUrl() {
        return baseUrl + MODELS_SUFFIX;
    }

    public String getFilesUrl() {
        return baseUrl + FILES_SUFFIX;
    }

    public String getApiKey() {
        return apiKey;
    }

    public ModelsList ListModels() throws IOException {

        okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
        okhttp3.Request request = new okhttp3.Request.Builder()
                .url(getModelsUrl())
                .addHeader("Authorization", "Bearer " + getApiKey())
                .build();
        try {
            okhttp3.Response response = client.newCall(request).execute();
            String body = response.body().string();
            Gson gson = new Gson();
            return gson.fromJson(body, ModelsList.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

    /*
        Mysql+Redis+Hbase+ES+Clickhouse
        Mysql 关系型数据库    底层基于b+
        1. 分析关系 进行见表 选择合适的存储引擎  binlog ，三藩市 ， 建模模型
        2. 字段，索引，表名，字段名 数据类型
        3. sql 查询 普通查询 条件 聚合 排序 关联+ 子查询 join union in（exist） 行转列 列传行
        4. 事务 事务特性 ，隔离级别，常见问题，Mvcc + 锁 实现隔离级别 mvcc 负责回滚等，数据可见性的判断 有些数据需要加锁锁定
        5. 视图 + 存储过程
        6. 集群  主从复制 ，读写分离，垂直分表，水平分表，库内，库外
        7. 优化 建表 ，引擎 索引 数据类型 ，查询的时候 explain 查看执行计划 type的级别 ，然后对索引进行对应优化，查询语句优化
        然后是服务层 可以 + redis缓存 + 减少对数据库的访问 ，主从复制 读写分离，垂直分表 水平分表
        8. 配置 innodb 缓存配置，io配置，内存配置，等等配置

        Clickhouse  OLAP 实时即席查询工具   底层基于合并树 合并树底层逻辑 + 特性
        1. 建表+存储引擎+排序字段+字段数据类型
        2. 查询  增删改查 避免少量删改
        3. 物化视图 + 数据去重
        4. 集群 副本表 + 分布式表
        5. 优化 explain 查看执行计划 常见问题 配置优化 建表优化 查询优化 物化视图优化 数据重复优化  数据不一致问题，同步问题

        Redis   基于内存的键值对数据库
        1. 数据类型 String, List, MAP, Set, zset
        2. 过期设置
        3. 淘汰策略
        4. 事务
        5. 集群   主从复制 读写分离 哨兵模式
        6. 优化 常见问题  内存优化，缓存穿透，缓存击穿，缓存雪崩


        宇宙级项目
            前端
                项目界面展示
            后端
                后台数据交互
                    Java
                        javase
                            面向对象，面向过程，函数式编程
                            类,抽象类,内部类,接口
                            方法+属性
                            关键字 + 可见性
                            计算符号+循环
                            io
                                3种类型
                                4大基类
                                20+衍生类
                                网络io
                                    bio
                                    nio
                                        模型
                                        零拷贝技术
                                            数据拷贝
                                            用户态+内核态的切换
                                    aio
                            集合

                            异常
                            反射
                            注解
                            泛型
                        jvm
                            类加载过程
                            JIT即时编译
                            架构
                                类加载器
                                    类加载器种类
                                    类加载流程
                                    双亲委派模型
                                    自定义类加载器
                                运行时数据区
                                    常量区
                                    方法区/元数据区
                                    堆内
                                        垃圾回收算法
                                            分区法
                                            分代法
                                            复制法
                                            标记整理法
                                            标记清除法

                                        垃圾回收器
                                            年轻代
                                            老年代
                                        GC
                                            mingc
                                            majorgc
                                            fullgc
                                            System，gc
                                         内存分配原则
                                        优化
                                    堆外
                                    程序计数器
                                    java虚拟机栈
                                    本地方法栈

                                计算引擎
                                本地方法库
                        juc
                            进程
                            线程
                            线程池
                            线程安全
                                同步阻塞
                                    Synized
                                    retranlock
                                    barrier
                                    countdownlock
                                    sappri
                                CAS
                                本地方案
                                    Threadlocal + volitel
                        设计模式
                            特性
                            创建型
                            建造型
                            行为型
                        数据结构
                            数组+链表
                            栈+队列
                            哈希+位图
                            树+图
                        算法
                            7大查找算法
                            10大排序算法
                            加密算法
                                对称加密
                                非对称加密
                            迭代+递归
                            BFS+DFS
                            贪心算法+动态规划
                       网络
                       硬件


            UI
                页面展示
            爬虫数据采集
                用户行为数据采集
                数据的采集
        数仓
            指标计算
                实时数仓
                    数据搭建
                    熟悉业务
                    数仓建模
                    指标计算
                离线数仓
                    数据搭建
                    熟悉业务
                    数仓建模
                    指标计算
               用户画像
                    标签建模
                    模型
                    打标签
                    调度
            推荐系统
                相似度计算
                协同过滤
                基于内容
        数据采集
            Flume，FlileBeat，Logstash
            Kafka，Polsar，RoketMq，ActiveMQ
            FlinkCDC,Canal，Maxwell
            datax，sqoop
        计算引擎
            Hive
            Spark
            Flink
        数据存储
            mysql,oracle
            riedis，memcache
            hbase，kudu
            clickhouse，impala，presto
        资源调度
            hadoop
        分布式协调
            zookeeper
        任务调度
            dolphinssheduler， azkaban，ozzie，airflow
        可视化展示
            帆软，quickbi，datav，superset
        集群监控 指标展示问题
        zibbix/promisis  + grafafa
        指标监控及展示问题
        元数据管理+权限管理
        数据质量监控
        数据质量治理
     */


    public ChatCompletionResponse ChatCompletion(ChatCompletionRequest request) throws IOException {
        request.stream = false;
        okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
        okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType, new Gson().toJson(request));
        okhttp3.Request httpRequest = new okhttp3.Request.Builder()
                .url(getChatCompletionUrl())
                .addHeader("Authorization", "Bearer " + getApiKey())
                .addHeader("Content-Type", "application/json")
                .post(body)
                .build();
        try {
            okhttp3.Response response = client.newCall(httpRequest).execute();
            String responseBody = response.body().string();
            System.out.println(responseBody);
            Gson gson = new Gson();
            return gson.fromJson(responseBody, ChatCompletionResponse.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

    // return a stream of ChatCompletionStreamResponse
    public Flowable<ChatCompletionStreamResponse> ChatCompletionStream(ChatCompletionRequest request) throws IOException {
        request.stream = true;
        okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
        okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType, new Gson().toJson(request));
        okhttp3.Request httpRequest = new okhttp3.Request.Builder()
                .url(getChatCompletionUrl())
                .addHeader("Authorization", "Bearer " + getApiKey())
                .addHeader("Content-Type", "application/json")
                .post(body)
                .build();
        okhttp3.Response response = client.newCall(httpRequest).execute();
        if (response.code() != 200) {
            throw new RuntimeException("Failed to start stream: " + response.body().string());
        }

        // get response body line by line
        return Flowable.create(emitter -> {
            okhttp3.ResponseBody responseBody = response.body();
            if (responseBody == null) {
                emitter.onError(new RuntimeException("Response body is null"));
                return;
            }
            String line;
            while ((line = responseBody.source().readUtf8Line()) != null) {
                if (line.startsWith("data:")) {
                    line = line.substring(5);
                    line = line.trim();
                }
                if (Objects.equals(line, "[DONE]")) {
                    emitter.onComplete();
                    return;
                }
                line = line.trim();
                if (line.isEmpty()) {
                    continue;
                }
                Gson gson = new Gson();
                ChatCompletionStreamResponse streamResponse = gson.fromJson(line, ChatCompletionStreamResponse.class);
                emitter.onNext(streamResponse);
            }
            emitter.onComplete();
        }, BackpressureStrategy.BUFFER);
    }
}

public class HelloWorld {
    public static String chat(String question) {
//        String apiKey = System.getenv("MOONSHOT_API_KEY");
        //1. 获得key
        String apiKey = "sk-u6IsCjHXdkRGCLHmYYtAMJx45PtJWeF7A3KLaTzpetugV5y7";
        //2. 如果key为null输出
        if (apiKey == null) {
            System.out.println("Please set MOONSHOT_API_KEY env");
            return "";
        }
        //3. 创建client对象
        Client client = new Client(apiKey);
        try {
            ModelsList models = client.ListModels();
            for (Model model : models.getData()) {
                System.out.println(model.getId());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //4. 进行提问
        final List<ChatCompletionMessage> messages = List.of(
//                new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
//                        "你是 Kimi，由 Moonshot AI 提供的人工智能助手，你更擅长中文和英文的对话。你会为用户提供安全，有帮助，准确的回答。同时，你会拒绝一些涉及恐怖主义，种族歧视，黄色暴力等问题的回答。Moonshot AI 为专有名词，不可翻译成其他语言。"),
//                new ChatCompletionMessage(ChatMessageRole.USER.value(),
//                        "你好，我叫李雷，1+1等于多少？"),
                new ChatCompletionMessage(ChatMessageRole.USER.value(),
                        question)
        );

//        try {
//            ChatCompletionResponse response = client.ChatCompletion(new ChatCompletionRequest(
//                    "moonshot-v1-8k",
//                    messages,
//                    50,
//                    0.3f,
//                    1
//            ));
//            for (ChatCompletionChoice choice : response.getChoices()) {
//                System.out.println(choice.getMessage().getContent());
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        //数据返回以及数据拼接
        StringBuilder stringBuilder = new StringBuilder();

        try {
            client.ChatCompletionStream(new ChatCompletionRequest(
                    "moonshot-v1-32k",
                    messages,
                    50,
                    0.3f,
                    1
            )).subscribe(
                    streamResponse -> {
                        if (streamResponse.getChoices().isEmpty()) {
                            return;
                        }
                        for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
                            String finishReason = choice.getFinishReason();
                            if (finishReason != null) {
                                System.out.println("finish reason: " + finishReason);
                                continue;
                            }
//                            System.out.println(choice.getDelta().getContent());
                            stringBuilder.append(choice.getDelta().getContent());
                        }

                    },
                    error -> {
                        error.printStackTrace();
                    },
                    () -> {
                        System.out.println("complete");
                    }
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

        String s = stringBuilder.toString();
        System.out.println(s);
return s;
    }

}